home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Introduction to 3D Game …ogramming with DirectX 12
/
Introduction-to-3D-Game-Programming-with-DirectX-12.ISO
/
Code.Textures
/
Chapter 23 Character Animation
/
SkinnedMesh
/
SkinnedMeshApp.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
2016-03-02
|
78KB
|
1,963 lines
//***************************************************************************************
// SkinnedMeshApp.cpp by Frank Luna (C) 2015 All Rights Reserved.
//***************************************************************************************
#include "../../Common/d3dApp.h"
#include "../../Common/MathHelper.h"
#include "../../Common/UploadBuffer.h"
#include "../../Common/GeometryGenerator.h"
#include "../../Common/Camera.h"
#include "FrameResource.h"
#include "ShadowMap.h"
#include "Ssao.h"
#include "SkinnedData.h"
#include "LoadM3d.h"
using Microsoft::WRL::ComPtr;
using namespace DirectX;
using namespace DirectX::PackedVector;
const int gNumFrameResources = 3;
struct SkinnedModelInstance
{
SkinnedData* SkinnedInfo = nullptr;
std::vector<DirectX::XMFLOAT4X4> FinalTransforms;
std::string ClipName;
float TimePos = 0.0f;
// Called every frame and increments the time position, interpolates the
// animations for each bone based on the current animation clip, and
// generates the final transforms which are ultimately set to the effect
// for processing in the vertex shader.
void UpdateSkinnedAnimation(float dt)
{
TimePos += dt;
// Loop animation
if(TimePos > SkinnedInfo->GetClipEndTime(ClipName))
TimePos = 0.0f;
// Compute the final transforms for this time position.
SkinnedInfo->GetFinalTransforms(ClipName, TimePos, FinalTransforms);
}
};
// Lightweight structure stores parameters to draw a shape. This will
// vary from app-to-app.
struct RenderItem
{
RenderItem() = default;
RenderItem(const RenderItem& rhs) = delete;
// World matrix of the shape that describes the object's local space
// relative to the world space, which defines the position, orientation,
// and scale of the object in the world.
XMFLOAT4X4 World = MathHelper::Identity4x4();
XMFLOAT4X4 TexTransform = MathHelper::Identity4x4();
// Dirty flag indicating the object data has changed and we need to update the constant buffer.
// Because we have an object cbuffer for each FrameResource, we have to apply the
// update to each FrameResource. Thus, when we modify obect data we should set
// NumFramesDirty = gNumFrameResources so that each frame resource gets the update.
int NumFramesDirty = gNumFrameResources;
// Index into GPU constant buffer corresponding to the ObjectCB for this render item.
UINT ObjCBIndex = -1;
Material* Mat = nullptr;
MeshGeometry* Geo = nullptr;
// Primitive topology.
D3D12_PRIMITIVE_TOPOLOGY PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
// DrawIndexedInstanced parameters.
UINT IndexCount = 0;
UINT StartIndexLocation = 0;
int BaseVertexLocation = 0;
// Only applicable to skinned render-items.
UINT SkinnedCBIndex = -1;
// nullptr if this render-item is not animated by skinned mesh.
SkinnedModelInstance* SkinnedModelInst = nullptr;
};
enum class RenderLayer : int
{
Opaque = 0,
SkinnedOpaque,
Debug,
Sky,
Count
};
class SkinnedMeshApp : public D3DApp
{
public:
SkinnedMeshApp(HINSTANCE hInstance);
SkinnedMeshApp(const SkinnedMeshApp& rhs) = delete;
SkinnedMeshApp& operator=(const SkinnedMeshApp& rhs) = delete;
~SkinnedMeshApp();
virtual bool Initialize()override;
private:
virtual void CreateRtvAndDsvDescriptorHeaps()override;
virtual void OnResize()override;
virtual void Update(const GameTimer& gt)override;
virtual void Draw(const GameTimer& gt)override;
virtual void OnMouseDown(WPARAM btnState, int x, int y)override;
virtual void OnMouseUp(WPARAM btnState, int x, int y)override;
virtual void OnMouseMove(WPARAM btnState, int x, int y)override;
void OnKeyboardInput(const GameTimer& gt);
void AnimateMaterials(const GameTimer& gt);
void UpdateObjectCBs(const GameTimer& gt);
void UpdateSkinnedCBs(const GameTimer& gt);
void UpdateMaterialBuffer(const GameTimer& gt);
void UpdateShadowTransform(const GameTimer& gt);
void UpdateMainPassCB(const GameTimer& gt);
void UpdateShadowPassCB(const GameTimer& gt);
void UpdateSsaoCB(const GameTimer& gt);
void LoadTextures();
void BuildRootSignature();
void BuildSsaoRootSignature();
void BuildDescriptorHeaps();
void BuildShadersAndInputLayout();
void BuildShapeGeometry();
void LoadSkinnedModel();
void BuildPSOs();
void BuildFrameResources();
void BuildMaterials();
void BuildRenderItems();
void DrawRenderItems(ID3D12GraphicsCommandList* cmdList, const std::vector<RenderItem*>& ritems);
void DrawSceneToShadowMap();
void DrawNormalsAndDepth();
CD3DX12_CPU_DESCRIPTOR_HANDLE GetCpuSrv(int index)const;
CD3DX12_GPU_DESCRIPTOR_HANDLE GetGpuSrv(int index)const;
CD3DX12_CPU_DESCRIPTOR_HANDLE GetDsv(int index)const;
CD3DX12_CPU_DESCRIPTOR_HANDLE GetRtv(int index)const;
std::array<const CD3DX12_STATIC_SAMPLER_DESC, 7> GetStaticSamplers();
private:
std::vector<std::unique_ptr<FrameResource>> mFrameResources;
FrameResource* mCurrFrameResource = nullptr;
int mCurrFrameResourceIndex = 0;
ComPtr<ID3D12RootSignature> mRootSignature = nullptr;
ComPtr<ID3D12RootSignature> mSsaoRootSignature = nullptr;
ComPtr<ID3D12DescriptorHeap> mSrvDescriptorHeap = nullptr;
std::unordered_map<std::string, std::unique_ptr<MeshGeometry>> mGeometries;
std::unordered_map<std::string, std::unique_ptr<Material>> mMaterials;
std::unordered_map<std::string, std::unique_ptr<Texture>> mTextures;
std::unordered_map<std::string, ComPtr<ID3DBlob>> mShaders;
std::unordered_map<std::string, ComPtr<ID3D12PipelineState>> mPSOs;
std::vector<D3D12_INPUT_ELEMENT_DESC> mInputLayout;
std::vector<D3D12_INPUT_ELEMENT_DESC> mSkinnedInputLayout;
// List of all the render items.
std::vector<std::unique_ptr<RenderItem>> mAllRitems;
// Render items divided by PSO.
std::vector<RenderItem*> mRitemLayer[(int)RenderLayer::Count];
UINT mSkyTexHeapIndex = 0;
UINT mShadowMapHeapIndex = 0;
UINT mSsaoHeapIndexStart = 0;
UINT mSsaoAmbientMapIndex = 0;
UINT mNullCubeSrvIndex = 0;
UINT mNullTexSrvIndex1 = 0;
UINT mNullTexSrvIndex2 = 0;
CD3DX12_GPU_DESCRIPTOR_HANDLE mNullSrv;
PassConstants mMainPassCB; // index 0 of pass cbuffer.
PassConstants mShadowPassCB;// index 1 of pass cbuffer.
UINT mSkinnedSrvHeapStart = 0;
std::string mSkinnedModelFilename = "Models\\soldier.m3d";
std::unique_ptr<SkinnedModelInstance> mSkinnedModelInst;
SkinnedData mSkinnedInfo;
std::vector<M3DLoader::Subset> mSkinnedSubsets;
std::vector<M3DLoader::M3dMaterial> mSkinnedMats;
std::vector<std::string> mSkinnedTextureNames;
Camera mCamera;
std::unique_ptr<ShadowMap> mShadowMap;
std::unique_ptr<Ssao> mSsao;
DirectX::BoundingSphere mSceneBounds;
float mLightNearZ = 0.0f;
float mLightFarZ = 0.0f;
XMFLOAT3 mLightPosW;
XMFLOAT4X4 mLightView = MathHelper::Identity4x4();
XMFLOAT4X4 mLightProj = MathHelper::Identity4x4();
XMFLOAT4X4 mShadowTransform = MathHelper::Identity4x4();
float mLightRotationAngle = 0.0f;
XMFLOAT3 mBaseLightDirections[3] = {
XMFLOAT3(0.57735f, -0.57735f, 0.57735f),
XMFLOAT3(-0.57735f, -0.57735f, 0.57735f),
XMFLOAT3(0.0f, -0.707f, -0.707f)
};
XMFLOAT3 mRotatedLightDirections[3];
POINT mLastMousePos;
};
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance,
PSTR cmdLine, int showCmd)
{
// Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
try
{
SkinnedMeshApp theApp(hInstance);
if(!theApp.Initialize())
return 0;
return theApp.Run();
}
catch(DxException& e)
{
MessageBox(nullptr, e.ToString().c_str(), L"HR Failed", MB_OK);
return 0;
}
}
SkinnedMeshApp::SkinnedMeshApp(HINSTANCE hInstance)
: D3DApp(hInstance)
{
// Estimate the scene bounding sphere manually since we know how the scene was constructed.
// The grid is the "widest object" with a width of 20 and depth of 30.0f, and centered at
// the world space origin. In general, you need to loop over every world space vertex
// position and compute the bounding sphere.
mSceneBounds.Center = XMFLOAT3(0.0f, 0.0f, 0.0f);
mSceneBounds.Radius = sqrtf(10.0f*10.0f + 15.0f*15.0f);
}
SkinnedMeshApp::~SkinnedMeshApp()
{
if(md3dDevice != nullptr)
FlushCommandQueue();
}
bool SkinnedMeshApp::Initialize()
{
if(!D3DApp::Initialize())
return false;
// Reset the command list to prep for initialization commands.
ThrowIfFailed(mCommandList->Reset(mDirectCmdListAlloc.Get(), nullptr));
mCamera.SetPosition(0.0f, 2.0f, -15.0f);
mShadowMap = std::make_unique<ShadowMap>(md3dDevice.Get(),
2048, 2048);
mSsao = std::make_unique<Ssao>(
md3dDevice.Get(),
mCommandList.Get(),
mClientWidth, mClientHeight);
LoadSkinnedModel();
LoadTextures();
BuildRootSignature();
BuildSsaoRootSignature();
BuildDescriptorHeaps();
BuildShadersAndInputLayout();
BuildShapeGeometry();
BuildMaterials();
BuildRenderItems();
BuildFrameResources();
BuildPSOs();
mSsao->SetPSOs(mPSOs["ssao"].Get(), mPSOs["ssaoBlur"].Get());
// Execute the initialization commands.
ThrowIfFailed(mCommandList->Close());
ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };
mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);
// Wait until initialization is complete.
FlushCommandQueue();
return true;
}
void SkinnedMeshApp::CreateRtvAndDsvDescriptorHeaps()
{
// Add +1 for screen normal map, +2 for ambient maps.
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc;
rtvHeapDesc.NumDescriptors = SwapChainBufferCount + 3;
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
rtvHeapDesc.NodeMask = 0;
ThrowIfFailed(md3dDevice->CreateDescriptorHeap(
&rtvHeapDesc, IID_PPV_ARGS(mRtvHeap.GetAddressOf())));
// Add +1 DSV for shadow map.
D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc;
dsvHeapDesc.NumDescriptors = 2;
dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
dsvHeapDesc.NodeMask = 0;
ThrowIfFailed(md3dDevice->CreateDescriptorHeap(
&dsvHeapDesc, IID_PPV_ARGS(mDsvHeap.GetAddressOf())));
}
void SkinnedMeshApp::OnResize()
{
D3DApp::OnResize();
mCamera.SetLens(0.25f*MathHelper::Pi, AspectRatio(), 1.0f, 1000.0f);
if(mSsao != nullptr)
{
mSsao->OnResize(mClientWidth, mClientHeight);
// Resources changed, so need to rebuild descriptors.
mSsao->RebuildDescriptors(mDepthStencilBuffer.Get());
}
}
void SkinnedMeshApp::Update(const GameTimer& gt)
{
OnKeyboardInput(gt);
// Cycle through the circular frame resource array.
mCurrFrameResourceIndex = (mCurrFrameResourceIndex + 1) % gNumFrameResources;
mCurrFrameResource = mFrameResources[mCurrFrameResourceIndex].get();
// Has the GPU finished processing the commands of the current frame resource?
// If not, wait until the GPU has completed commands up to this fence point.
if(mCurrFrameResource->Fence != 0 && mFence->GetCompletedValue() < mCurrFrameResource->Fence)
{
HANDLE eventHandle = CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
ThrowIfFailed(mFence->SetEventOnCompletion(mCurrFrameResource->Fence, eventHandle));
WaitForSingleObject(eventHandle, INFINITE);
CloseHandle(eventHandle);
}
//
// Animate the lights (and hence shadows).
//
mLightRotationAngle += 0.1f*gt.DeltaTime();
XMMATRIX R = XMMatrixRotationY(mLightRotationAngle);
for(int i = 0; i < 3; ++i)
{
XMVECTOR lightDir = XMLoadFloat3(&mBaseLightDirections[i]);
lightDir = XMVector3TransformNormal(lightDir, R);
XMStoreFloat3(&mRotatedLightDirections[i], lightDir);
}
AnimateMaterials(gt);
UpdateObjectCBs(gt);
UpdateSkinnedCBs(gt);
UpdateMaterialBuffer(gt);
UpdateShadowTransform(gt);
UpdateMainPassCB(gt);
UpdateShadowPassCB(gt);
UpdateSsaoCB(gt);
}
void SkinnedMeshApp::Draw(const GameTimer& gt)
{
auto cmdListAlloc = mCurrFrameResource->CmdListAlloc;
// Reuse the memory associated with command recording.
// We can only reset when the associated command lists have finished execution on the GPU.
ThrowIfFailed(cmdListAlloc->Reset());
// A command list can be reset after it has been added to the command queue via ExecuteCommandList.
// Reusing the command list reuses memory.
ThrowIfFailed(mCommandList->Reset(cmdListAlloc.Get(), mPSOs["opaque"].Get()));
ID3D12DescriptorHeap* descriptorHeaps[] = { mSrvDescriptorHeap.Get() };
mCommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);
mCommandList->SetGraphicsRootSignature(mRootSignature.Get());
//
// Shadow map pass.
//
// Bind all the materials used in this scene. For structured buffers, we can bypass the heap and
// set as a root descriptor.
auto matBuffer = mCurrFrameResource->MaterialBuffer->Resource();
mCommandList->SetGraphicsRootShaderResourceView(3, matBuffer->GetGPUVirtualAddress());
// Bind null SRV for shadow map pass.
mCommandList->SetGraphicsRootDescriptorTable(4, mNullSrv);
// Bind all the textures used in this scene. Observe
// that we only have to specify the first descriptor in the table.
// The root signature knows how many descriptors are expected in the table.
mCommandList->SetGraphicsRootDescriptorTable(5, mSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart());
DrawSceneToShadowMap();
//
// Normal/depth pass.
//
DrawNormalsAndDepth();
//
//
//
mCommandList->SetGraphicsRootSignature(mSsaoRootSignature.Get());
mSsao->ComputeSsao(mCommandList.Get(), mCurrFrameResource, 2);
//
// Main rendering pass.
//
mCommandList->SetGraphicsRootSignature(mRootSignature.Get());
// Rebind state whenever graphics root signature changes.
// Bind all the materials used in this scene. For structured buffers, we can bypass the heap and
// set as a root descriptor.
matBuffer = mCurrFrameResource->MaterialBuffer->Resource();
mCommandList->SetGraphicsRootShaderResourceView(3, matBuffer->GetGPUVirtualAddress());
mCommandList->RSSetViewports(1, &mScreenViewport);
mCommandList->RSSetScissorRects(1, &mScissorRect);
// Indicate a state transition on the resource usage.
mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));
// Clear the back buffer and depth buffer.
mCommandList->ClearRenderTargetView(CurrentBackBufferView(), Colors::LightSteelBlue, 0, nullptr);
mCommandList->ClearDepthStencilView(DepthStencilView(), D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);
// Specify the buffers we are going to render to.
mCommandList->OMSetRenderTargets(1, &CurrentBackBufferView(), true, &DepthStencilView());
// Bind all the textures used in this scene. Observe
// that we only have to specify the first descriptor in the table.
// The root signature knows how many descriptors are expected in the table.
mCommandList->SetGraphicsRootDescriptorTable(5, mSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart());
auto passCB = mCurrFrameResource->PassCB->Resource();
mCommandList->SetGraphicsRootConstantBufferView(2, passCB->GetGPUVirtualAddress());
// Bind the sky cube map. For our demos, we just use one "world" cube map representing the environment
// from far away, so all objects will use the same cube map and we only need to set it once per-frame.
// If we wanted to use "local" cube maps, we would have to change them per-object, or dynamically
// index into an array of cube maps.
CD3DX12_GPU_DESCRIPTOR_HANDLE skyTexDescriptor(mSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart());
skyTexDescriptor.Offset(mSkyTexHeapIndex, mCbvSrvUavDescriptorSize);
mCommandList->SetGraphicsRootDescriptorTable(4, skyTexDescriptor);
mCommandList->SetPipelineState(mPSOs["opaque"].Get());
DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Opaque]);
mCommandList->SetPipelineState(mPSOs["skinnedOpaque"].Get());
DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::SkinnedOpaque]);
mCommandList->SetPipelineState(mPSOs["debug"].Get());
DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Debug]);
mCommandList->SetPipelineState(mPSOs["sky"].Get());
DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Sky]);
// Indicate a state transition on the resource usage.
mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));
// Done recording commands.
ThrowIfFailed(mCommandList->Close());
// Add the command list to the queue for execution.
ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };
mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);
// Swap the back and front buffers
ThrowIfFailed(mSwapChain->Present(0, 0));
mCurrBackBuffer = (mCurrBackBuffer + 1) % SwapChainBufferCount;
// Advance the fence value to mark commands up to this fence point.
mCurrFrameResource->Fence = ++mCurrentFence;
// Add an instruction to the command queue to set a new fence point.
// Because we are on the GPU timeline, the new fence point won't be
// set until the GPU finishes processing all the commands prior to this Signal().
mCommandQueue->Signal(mFence.Get(), mCurrentFence);
}
void SkinnedMeshApp::OnMouseDown(WPARAM btnState, int x, int y)
{
mLastMousePos.x = x;
mLastMousePos.y = y;
SetCapture(mhMainWnd);
}
void SkinnedMeshApp::OnMouseUp(WPARAM btnState, int x, int y)
{
ReleaseCapture();
}
void SkinnedMeshApp::OnMouseMove(WPARAM btnState, int x, int y)
{
if((btnState & MK_LBUTTON) != 0)
{
// Make each pixel correspond to a quarter of a degree.
float dx = XMConvertToRadians(0.25f*static_cast<float>(x - mLastMousePos.x));
float dy = XMConvertToRadians(0.25f*static_cast<float>(y - mLastMousePos.y));
mCamera.Pitch(dy);
mCamera.RotateY(dx);
}
mLastMousePos.x = x;
mLastMousePos.y = y;
}
void SkinnedMeshApp::OnKeyboardInput(const GameTimer& gt)
{
const float dt = gt.DeltaTime();
if(GetAsyncKeyState('W') & 0x8000)
mCamera.Walk(10.0f*dt);
if(GetAsyncKeyState('S') & 0x8000)
mCamera.Walk(-10.0f*dt);
if(GetAsyncKeyState('A') & 0x8000)
mCamera.Strafe(-10.0f*dt);
if(GetAsyncKeyState('D') & 0x8000)
mCamera.Strafe(10.0f*dt);
mCamera.UpdateViewMatrix();
}
void SkinnedMeshApp::AnimateMaterials(const GameTimer& gt)
{
}
void SkinnedMeshApp::UpdateObjectCBs(const GameTimer& gt)
{
auto currObjectCB = mCurrFrameResource->ObjectCB.get();
for(auto& e : mAllRitems)
{
// Only update the cbuffer data if the constants have changed.
// This needs to be tracked per frame resource.
if(e->NumFramesDirty > 0)
{
XMMATRIX world = XMLoadFloat4x4(&e->World);
XMMATRIX texTransform = XMLoadFloat4x4(&e->TexTransform);
ObjectConstants objConstants;
XMStoreFloat4x4(&objConstants.World, XMMatrixTranspose(world));
XMStoreFloat4x4(&objConstants.TexTransform, XMMatrixTranspose(texTransform));
objConstants.MaterialIndex = e->Mat->MatCBIndex;
currObjectCB->CopyData(e->ObjCBIndex, objConstants);
// Next FrameResource need to be updated too.
e->NumFramesDirty--;
}
}
}
void SkinnedMeshApp::UpdateSkinnedCBs(const GameTimer& gt)
{
auto currSkinnedCB = mCurrFrameResource->SkinnedCB.get();
// We only have one skinned model being animated.
mSkinnedModelInst->UpdateSkinnedAnimation(gt.DeltaTime());
SkinnedConstants skinnedConstants;
std::copy(
std::begin(mSkinnedModelInst->FinalTransforms),
std::end(mSkinnedModelInst->FinalTransforms),
&skinnedConstants.BoneTransforms[0]);
currSkinnedCB->CopyData(0, skinnedConstants);
}
void SkinnedMeshApp::UpdateMaterialBuffer(const GameTimer& gt)
{
auto currMaterialBuffer = mCurrFrameResource->MaterialBuffer.get();
for(auto& e : mMaterials)
{
// Only update the cbuffer data if the constants have changed. If the cbuffer
// data changes, it needs to be updated for each FrameResource.
Material* mat = e.second.get();
if(mat->NumFramesDirty > 0)
{
XMMATRIX matTransform = XMLoadFloat4x4(&mat->MatTransform);
MaterialData matData;
matData.DiffuseAlbedo = mat->DiffuseAlbedo;
matData.FresnelR0 = mat->FresnelR0;
matData.Roughness = mat->Roughness;
XMStoreFloat4x4(&matData.MatTransform, XMMatrixTranspose(matTransform));
matData.DiffuseMapIndex = mat->DiffuseSrvHeapIndex;
matData.NormalMapIndex = mat->NormalSrvHeapIndex;
currMaterialBuffer->CopyData(mat->MatCBIndex, matData);
// Next FrameResource need to be updated too.
mat->NumFramesDirty--;
}
}
}
void SkinnedMeshApp::UpdateShadowTransform(const GameTimer& gt)
{
// Only the first "main" light casts a shadow.
XMVECTOR lightDir = XMLoadFloat3(&mRotatedLightDirections[0]);
XMVECTOR lightPos = -2.0f*mSceneBounds.Radius*lightDir;
XMVECTOR targetPos = XMLoadFloat3(&mSceneBounds.Center);
XMVECTOR lightUp = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
XMMATRIX lightView = XMMatrixLookAtLH(lightPos, targetPos, lightUp);
XMStoreFloat3(&mLightPosW, lightPos);
// Transform bounding sphere to light space.
XMFLOAT3 sphereCenterLS;
XMStoreFloat3(&sphereCenterLS, XMVector3TransformCoord(targetPos, lightView));
// Ortho frustum in light space encloses scene.
float l = sphereCenterLS.x - mSceneBounds.Radius;
float b = sphereCenterLS.y - mSceneBounds.Radius;
float n = sphereCenterLS.z - mSceneBounds.Radius;
float r = sphereCenterLS.x + mSceneBounds.Radius;
float t = sphereCenterLS.y + mSceneBounds.Radius;
float f = sphereCenterLS.z + mSceneBounds.Radius;
mLightNearZ = n;
mLightFarZ = f;
XMMATRIX lightProj = XMMatrixOrthographicOffCenterLH(l, r, b, t, n, f);
// Transform NDC space [-1,+1]^2 to texture space [0,1]^2
XMMATRIX T(
0.5f, 0.0f, 0.0f, 0.0f,
0.0f, -0.5f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f);
XMMATRIX S = lightView*lightProj*T;
XMStoreFloat4x4(&mLightView, lightView);
XMStoreFloat4x4(&mLightProj, lightProj);
XMStoreFloat4x4(&mShadowTransform, S);
}
void SkinnedMeshApp::UpdateMainPassCB(const GameTimer& gt)
{
XMMATRIX view = mCamera.GetView();
XMMATRIX proj = mCamera.GetProj();
XMMATRIX viewProj = XMMatrixMultiply(view, proj);
XMMATRIX invView = XMMatrixInverse(&XMMatrixDeterminant(view), view);
XMMATRIX invProj = XMMatrixInverse(&XMMatrixDeterminant(proj), proj);
XMMATRIX invViewProj = XMMatrixInverse(&XMMatrixDeterminant(viewProj), viewProj);
// Transform NDC space [-1,+1]^2 to texture space [0,1]^2
XMMATRIX T(
0.5f, 0.0f, 0.0f, 0.0f,
0.0f, -0.5f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f);
XMMATRIX viewProjTex = XMMatrixMultiply(viewProj, T);
XMMATRIX shadowTransform = XMLoadFloat4x4(&mShadowTransform);
XMStoreFloat4x4(&mMainPassCB.View, XMMatrixTranspose(view));
XMStoreFloat4x4(&mMainPassCB.InvView, XMMatrixTranspose(invView));
XMStoreFloat4x4(&mMainPassCB.Proj, XMMatrixTranspose(proj));
XMStoreFloat4x4(&mMainPassCB.InvProj, XMMatrixTranspose(invProj));
XMStoreFloat4x4(&mMainPassCB.ViewProj, XMMatrixTranspose(viewProj));
XMStoreFloat4x4(&mMainPassCB.InvViewProj, XMMatrixTranspose(invViewProj));
XMStoreFloat4x4(&mMainPassCB.ViewProjTex, XMMatrixTranspose(viewProjTex));
XMStoreFloat4x4(&mMainPassCB.ShadowTransform, XMMatrixTranspose(shadowTransform));
mMainPassCB.EyePosW = mCamera.GetPosition3f();
mMainPassCB.RenderTargetSize = XMFLOAT2((float)mClientWidth, (float)mClientHeight);
mMainPassCB.InvRenderTargetSize = XMFLOAT2(1.0f / mClientWidth, 1.0f / mClientHeight);
mMainPassCB.NearZ = 1.0f;
mMainPassCB.FarZ = 1000.0f;
mMainPassCB.TotalTime = gt.TotalTime();
mMainPassCB.DeltaTime = gt.DeltaTime();
mMainPassCB.AmbientLight = { 0.25f, 0.25f, 0.35f, 1.0f };
mMainPassCB.Lights[0].Direction = mRotatedLightDirections[0];
mMainPassCB.Lights[0].Strength = { 0.9f, 0.9f, 0.7f };
mMainPassCB.Lights[1].Direction = mRotatedLightDirections[1];
mMainPassCB.Lights[1].Strength = { 0.4f, 0.4f, 0.4f };
mMainPassCB.Lights[2].Direction = mRotatedLightDirections[2];
mMainPassCB.Lights[2].Strength = { 0.2f, 0.2f, 0.2f };
auto currPassCB = mCurrFrameResource->PassCB.get();
currPassCB->CopyData(0, mMainPassCB);
}
void SkinnedMeshApp::UpdateShadowPassCB(const GameTimer& gt)
{
XMMATRIX view = XMLoadFloat4x4(&mLightView);
XMMATRIX proj = XMLoadFloat4x4(&mLightProj);
XMMATRIX viewProj = XMMatrixMultiply(view, proj);
XMMATRIX invView = XMMatrixInverse(&XMMatrixDeterminant(view), view);
XMMATRIX invProj = XMMatrixInverse(&XMMatrixDeterminant(proj), proj);
XMMATRIX invViewProj = XMMatrixInverse(&XMMatrixDeterminant(viewProj), viewProj);
UINT w = mShadowMap->Width();
UINT h = mShadowMap->Height();
XMStoreFloat4x4(&mShadowPassCB.View, XMMatrixTranspose(view));
XMStoreFloat4x4(&mShadowPassCB.InvView, XMMatrixTranspose(invView));
XMStoreFloat4x4(&mShadowPassCB.Proj, XMMatrixTranspose(proj));
XMStoreFloat4x4(&mShadowPassCB.InvProj, XMMatrixTranspose(invProj));
XMStoreFloat4x4(&mShadowPassCB.ViewProj, XMMatrixTranspose(viewProj));
XMStoreFloat4x4(&mShadowPassCB.InvViewProj, XMMatrixTranspose(invViewProj));
mShadowPassCB.EyePosW = mLightPosW;
mShadowPassCB.RenderTargetSize = XMFLOAT2((float)w, (float)h);
mShadowPassCB.InvRenderTargetSize = XMFLOAT2(1.0f / w, 1.0f / h);
mShadowPassCB.NearZ = mLightNearZ;
mShadowPassCB.FarZ = mLightFarZ;
auto currPassCB = mCurrFrameResource->PassCB.get();
currPassCB->CopyData(1, mShadowPassCB);
}
void SkinnedMeshApp::UpdateSsaoCB(const GameTimer& gt)
{
SsaoConstants ssaoCB;
XMMATRIX P = mCamera.GetProj();
// Transform NDC space [-1,+1]^2 to texture space [0,1]^2
XMMATRIX T(
0.5f, 0.0f, 0.0f, 0.0f,
0.0f, -0.5f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f);
ssaoCB.Proj = mMainPassCB.Proj;
ssaoCB.InvProj = mMainPassCB.InvProj;
XMStoreFloat4x4(&ssaoCB.ProjTex, XMMatrixTranspose(P*T));
mSsao->GetOffsetVectors(ssaoCB.OffsetVectors);
auto blurWeights = mSsao->CalcGaussWeights(2.5f);
ssaoCB.BlurWeights[0] = XMFLOAT4(&blurWeights[0]);
ssaoCB.BlurWeights[1] = XMFLOAT4(&blurWeights[4]);
ssaoCB.BlurWeights[2] = XMFLOAT4(&blurWeights[8]);
ssaoCB.InvRenderTargetSize = XMFLOAT2(1.0f / mSsao->SsaoMapWidth(), 1.0f / mSsao->SsaoMapHeight());
// Coordinates given in view space.
ssaoCB.OcclusionRadius = 0.5f;
ssaoCB.OcclusionFadeStart = 0.2f;
ssaoCB.OcclusionFadeEnd = 2.0f;
ssaoCB.SurfaceEpsilon = 0.05f;
auto currSsaoCB = mCurrFrameResource->SsaoCB.get();
currSsaoCB->CopyData(0, ssaoCB);
}
void SkinnedMeshApp::LoadTextures()
{
std::vector<std::string> texNames =
{
"bricksDiffuseMap",
"bricksNormalMap",
"tileDiffuseMap",
"tileNormalMap",
"defaultDiffuseMap",
"defaultNormalMap",
"skyCubeMap"
};
std::vector<std::wstring> texFilenames =
{
L"../../Textures/bricks2.dds",
L"../../Textures/bricks2_nmap.dds",
L"../../Textures/tile.dds",
L"../../Textures/tile_nmap.dds",
L"../../Textures/white1x1.dds",
L"../../Textures/default_nmap.dds",
L"../../Textures/desertcube1024.dds"
};
// Add skinned model textures to list so we can reference by name later.
for(UINT i = 0; i < mSkinnedMats.size(); ++i)
{
std::string diffuseName = mSkinnedMats[i].DiffuseMapName;
std::string normalName = mSkinnedMats[i].NormalMapName;
std::wstring diffuseFilename = L"../../Textures/" + AnsiToWString(diffuseName);
std::wstring normalFilename = L"../../Textures/" + AnsiToWString(normalName);
// strip off extension
diffuseName = diffuseName.substr(0, diffuseName.find_last_of("."));
normalName = normalName.substr(0, normalName.find_last_of("."));
mSkinnedTextureNames.push_back(diffuseName);
texNames.push_back(diffuseName);
texFilenames.push_back(diffuseFilename);
mSkinnedTextureNames.push_back(normalName);
texNames.push_back(normalName);
texFilenames.push_back(normalFilename);
}
for(int i = 0; i < (int)texNames.size(); ++i)
{
// Don't create duplicates.
if(mTextures.find(texNames[i]) == std::end(mTextures))
{
auto texMap = std::make_unique<Texture>();
texMap->Name = texNames[i];
texMap->Filename = texFilenames[i];
ThrowIfFailed(DirectX::CreateDDSTextureFromFile12(md3dDevice.Get(),
mCommandList.Get(), texMap->Filename.c_str(),
texMap->Resource, texMap->UploadHeap));
mTextures[texMap->Name] = std::move(texMap);
}
}
}
void SkinnedMeshApp::BuildRootSignature()
{
CD3DX12_DESCRIPTOR_RANGE texTable0;
texTable0.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 3, 0, 0);
CD3DX12_DESCRIPTOR_RANGE texTable1;
texTable1.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 48, 3, 0);
// Root parameter can be a table, root descriptor or root constants.
CD3DX12_ROOT_PARAMETER slotRootParameter[6];
// Perfomance TIP: Order from most frequent to least frequent.
slotRootParameter[0].InitAsConstantBufferView(0);
slotRootParameter[1].InitAsConstantBufferView(1);
slotRootParameter[2].InitAsConstantBufferView(2);
slotRootParameter[3].InitAsShaderResourceView(0, 1);
slotRootParameter[4].InitAsDescriptorTable(1, &texTable0, D3D12_SHADER_VISIBILITY_PIXEL);
slotRootParameter[5].InitAsDescriptorTable(1, &texTable1, D3D12_SHADER_VISIBILITY_PIXEL);
auto staticSamplers = GetStaticSamplers();
// A root signature is an array of root parameters.
CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(6, slotRootParameter,
(UINT)staticSamplers.size(), staticSamplers.data(),
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
// create a root signature with a single slot which points to a descriptor range consisting of a single constant buffer
ComPtr<ID3DBlob> serializedRootSig = nullptr;
ComPtr<ID3DBlob> errorBlob = nullptr;
HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1,
serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());
if(errorBlob != nullptr)
{
::OutputDebugStringA((char*)errorBlob->GetBufferPointer());
}
ThrowIfFailed(hr);
ThrowIfFailed(md3dDevice->CreateRootSignature(
0,
serializedRootSig->GetBufferPointer(),
serializedRootSig->GetBufferSize(),
IID_PPV_ARGS(mRootSignature.GetAddressOf())));
}
void SkinnedMeshApp::BuildSsaoRootSignature()
{
CD3DX12_DESCRIPTOR_RANGE texTable0;
texTable0.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 2, 0, 0);
CD3DX12_DESCRIPTOR_RANGE texTable1;
texTable1.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 2, 0);
// Root parameter can be a table, root descriptor or root constants.
CD3DX12_ROOT_PARAMETER slotRootParameter[4];
// Perfomance TIP: Order from most frequent to least frequent.
slotRootParameter[0].InitAsConstantBufferView(0);
slotRootParameter[1].InitAsConstants(1, 1);
slotRootParameter[2].InitAsDescriptorTable(1, &texTable0, D3D12_SHADER_VISIBILITY_PIXEL);
slotRootParameter[3].InitAsDescriptorTable(1, &texTable1, D3D12_SHADER_VISIBILITY_PIXEL);
const CD3DX12_STATIC_SAMPLER_DESC pointClamp(
0, // shaderRegister
D3D12_FILTER_MIN_MAG_MIP_POINT, // filter
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_CLAMP); // addressW
const CD3DX12_STATIC_SAMPLER_DESC linearClamp(
1, // shaderRegister
D3D12_FILTER_MIN_MAG_MIP_LINEAR, // filter
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_CLAMP); // addressW
const CD3DX12_STATIC_SAMPLER_DESC depthMapSam(
2, // shaderRegister
D3D12_FILTER_MIN_MAG_MIP_LINEAR, // filter
D3D12_TEXTURE_ADDRESS_MODE_BORDER, // addressU
D3D12_TEXTURE_ADDRESS_MODE_BORDER, // addressV
D3D12_TEXTURE_ADDRESS_MODE_BORDER, // addressW
0.0f,
0,
D3D12_COMPARISON_FUNC_LESS_EQUAL,
D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE);
const CD3DX12_STATIC_SAMPLER_DESC linearWrap(
3, // shaderRegister
D3D12_FILTER_MIN_MAG_MIP_LINEAR, // filter
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_WRAP); // addressW
std::array<CD3DX12_STATIC_SAMPLER_DESC, 4> staticSamplers =
{
pointClamp, linearClamp, depthMapSam, linearWrap
};
// A root signature is an array of root parameters.
CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(4, slotRootParameter,
(UINT)staticSamplers.size(), staticSamplers.data(),
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
// create a root signature with a single slot which points to a descriptor range consisting of a single constant buffer
ComPtr<ID3DBlob> serializedRootSig = nullptr;
ComPtr<ID3DBlob> errorBlob = nullptr;
HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1,
serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());
if(errorBlob != nullptr)
{
::OutputDebugStringA((char*)errorBlob->GetBufferPointer());
}
ThrowIfFailed(hr);
ThrowIfFailed(md3dDevice->CreateRootSignature(
0,
serializedRootSig->GetBufferPointer(),
serializedRootSig->GetBufferSize(),
IID_PPV_ARGS(mSsaoRootSignature.GetAddressOf())));
}
void SkinnedMeshApp::BuildDescriptorHeaps()
{
//
// Create the SRV heap.
//
D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
srvHeapDesc.NumDescriptors = 64;
srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(&mSrvDescriptorHeap)));
//
// Fill out the heap with actual descriptors.
//
CD3DX12_CPU_DESCRIPTOR_HANDLE hDescriptor(mSrvDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
std::vector<ComPtr<ID3D12Resource>> tex2DList =
{
mTextures["bricksDiffuseMap"]->Resource,
mTextures["bricksNormalMap"]->Resource,
mTextures["tileDiffuseMap"]->Resource,
mTextures["tileNormalMap"]->Resource,
mTextures["defaultDiffuseMap"]->Resource,
mTextures["defaultNormalMap"]->Resource
};
mSkinnedSrvHeapStart = (UINT)tex2DList.size();
for(UINT i = 0; i < (UINT)mSkinnedTextureNames.size(); ++i)
{
auto texResource = mTextures[mSkinnedTextureNames[i]]->Resource;
assert(texResource != nullptr);
tex2DList.push_back(texResource);
}
auto skyCubeMap = mTextures["skyCubeMap"]->Resource;
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MostDetailedMip = 0;
srvDesc.Texture2D.ResourceMinLODClamp = 0.0f;
for(UINT i = 0; i < (UINT)tex2DList.size(); ++i)
{
srvDesc.Format = tex2DList[i]->GetDesc().Format;
srvDesc.Texture2D.MipLevels = tex2DList[i]->GetDesc().MipLevels;
md3dDevice->CreateShaderResourceView(tex2DList[i].Get(), &srvDesc, hDescriptor);
// next descriptor
hDescriptor.Offset(1, mCbvSrvUavDescriptorSize);
}
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
srvDesc.TextureCube.MostDetailedMip = 0;
srvDesc.TextureCube.MipLevels = skyCubeMap->GetDesc().MipLevels;
srvDesc.TextureCube.ResourceMinLODClamp = 0.0f;
srvDesc.Format = skyCubeMap->GetDesc().Format;
md3dDevice->CreateShaderResourceView(skyCubeMap.Get(), &srvDesc, hDescriptor);
mSkyTexHeapIndex = (UINT)tex2DList.size();
mShadowMapHeapIndex = mSkyTexHeapIndex + 1;
mSsaoHeapIndexStart = mShadowMapHeapIndex + 1;
mSsaoAmbientMapIndex = mSsaoHeapIndexStart + 3;
mNullCubeSrvIndex = mSsaoHeapIndexStart + 5;
mNullTexSrvIndex1 = mNullCubeSrvIndex + 1;
mNullTexSrvIndex2 = mNullTexSrvIndex1 + 1;
auto nullSrv = GetCpuSrv(mNullCubeSrvIndex);
mNullSrv = GetGpuSrv(mNullCubeSrvIndex);
md3dDevice->CreateShaderResourceView(nullptr, &srvDesc, nullSrv);
nullSrv.Offset(1, mCbvSrvUavDescriptorSize);
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srvDesc.Texture2D.MostDetailedMip = 0;
srvDesc.Texture2D.MipLevels = 1;
srvDesc.Texture2D.ResourceMinLODClamp = 0.0f;
md3dDevice->CreateShaderResourceView(nullptr, &srvDesc, nullSrv);
nullSrv.Offset(1, mCbvSrvUavDescriptorSize);
md3dDevice->CreateShaderResourceView(nullptr, &srvDesc, nullSrv);
mShadowMap->BuildDescriptors(
GetCpuSrv(mShadowMapHeapIndex),
GetGpuSrv(mShadowMapHeapIndex),
GetDsv(1));
mSsao->BuildDescriptors(
mDepthStencilBuffer.Get(),
GetCpuSrv(mSsaoHeapIndexStart),
GetGpuSrv(mSsaoHeapIndexStart),
GetRtv(SwapChainBufferCount),
mCbvSrvUavDescriptorSize,
mRtvDescriptorSize);
}
void SkinnedMeshApp::BuildShadersAndInputLayout()
{
const D3D_SHADER_MACRO alphaTestDefines[] =
{
"ALPHA_TEST", "1",
NULL, NULL
};
const D3D_SHADER_MACRO skinnedDefines[] =
{
"SKINNED", "1",
NULL, NULL
};
mShaders["standardVS"] = d3dUtil::CompileShader(L"Shaders\\Default.hlsl", nullptr, "VS", "vs_5_1");
mShaders["skinnedVS"] = d3dUtil::CompileShader(L"Shaders\\Default.hlsl", skinnedDefines, "VS", "vs_5_1");
mShaders["opaquePS"] = d3dUtil::CompileShader(L"Shaders\\Default.hlsl", nullptr, "PS", "ps_5_1");
mShaders["shadowVS"] = d3dUtil::CompileShader(L"Shaders\\Shadows.hlsl", nullptr, "VS", "vs_5_1");
mShaders["skinnedShadowVS"] = d3dUtil::CompileShader(L"Shaders\\Shadows.hlsl", skinnedDefines, "VS", "vs_5_1");
mShaders["shadowOpaquePS"] = d3dUtil::CompileShader(L"Shaders\\Shadows.hlsl", nullptr, "PS", "ps_5_1");
mShaders["shadowAlphaTestedPS"] = d3dUtil::CompileShader(L"Shaders\\Shadows.hlsl", alphaTestDefines, "PS", "ps_5_1");
mShaders["debugVS"] = d3dUtil::CompileShader(L"Shaders\\ShadowDebug.hlsl", nullptr, "VS", "vs_5_1");
mShaders["debugPS"] = d3dUtil::CompileShader(L"Shaders\\ShadowDebug.hlsl", nullptr, "PS", "ps_5_1");
mShaders["drawNormalsVS"] = d3dUtil::CompileShader(L"Shaders\\DrawNormals.hlsl", nullptr, "VS", "vs_5_1");
mShaders["skinnedDrawNormalsVS"] = d3dUtil::CompileShader(L"Shaders\\DrawNormals.hlsl", skinnedDefines, "VS", "vs_5_1");
mShaders["drawNormalsPS"] = d3dUtil::CompileShader(L"Shaders\\DrawNormals.hlsl", nullptr, "PS", "ps_5_1");
mShaders["ssaoVS"] = d3dUtil::CompileShader(L"Shaders\\Ssao.hlsl", nullptr, "VS", "vs_5_1");
mShaders["ssaoPS"] = d3dUtil::CompileShader(L"Shaders\\Ssao.hlsl", nullptr, "PS", "ps_5_1");
mShaders["ssaoBlurVS"] = d3dUtil::CompileShader(L"Shaders\\SsaoBlur.hlsl", nullptr, "VS", "vs_5_1");
mShaders["ssaoBlurPS"] = d3dUtil::CompileShader(L"Shaders\\SsaoBlur.hlsl", nullptr, "PS", "ps_5_1");
mShaders["skyVS"] = d3dUtil::CompileShader(L"Shaders\\Sky.hlsl", nullptr, "VS", "vs_5_1");
mShaders["skyPS"] = d3dUtil::CompileShader(L"Shaders\\Sky.hlsl", nullptr, "PS", "ps_5_1");
mInputLayout =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 32, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
};
mSkinnedInputLayout =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 32, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "WEIGHTS", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 44, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "BONEINDICES", 0, DXGI_FORMAT_R8G8B8A8_UINT, 0, 56, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
};
}
void SkinnedMeshApp::BuildShapeGeometry()
{
GeometryGenerator geoGen;
GeometryGenerator::MeshData box = geoGen.CreateBox(1.0f, 1.0f, 1.0f, 3);
GeometryGenerator::MeshData grid = geoGen.CreateGrid(20.0f, 30.0f, 60, 40);
GeometryGenerator::MeshData sphere = geoGen.CreateSphere(0.5f, 20, 20);
GeometryGenerator::MeshData cylinder = geoGen.CreateCylinder(0.5f, 0.3f, 3.0f, 20, 20);
GeometryGenerator::MeshData quad = geoGen.CreateQuad(0.0f, 0.0f, 1.0f, 1.0f, 0.0f);
//
// We are concatenating all the geometry into one big vertex/index buffer. So
// define the regions in the buffer each submesh covers.
//
// Cache the vertex offsets to each object in the concatenated vertex buffer.
UINT boxVertexOffset = 0;
UINT gridVertexOffset = (UINT)box.Vertices.size();
UINT sphereVertexOffset = gridVertexOffset + (UINT)grid.Vertices.size();
UINT cylinderVertexOffset = sphereVertexOffset + (UINT)sphere.Vertices.size();
UINT quadVertexOffset = cylinderVertexOffset + (UINT)cylinder.Vertices.size();
// Cache the starting index for each object in the concatenated index buffer.
UINT boxIndexOffset = 0;
UINT gridIndexOffset = (UINT)box.Indices32.size();
UINT sphereIndexOffset = gridIndexOffset + (UINT)grid.Indices32.size();
UINT cylinderIndexOffset = sphereIndexOffset + (UINT)sphere.Indices32.size();
UINT quadIndexOffset = cylinderIndexOffset + (UINT)cylinder.Indices32.size();
SubmeshGeometry boxSubmesh;
boxSubmesh.IndexCount = (UINT)box.Indices32.size();
boxSubmesh.StartIndexLocation = boxIndexOffset;
boxSubmesh.BaseVertexLocation = boxVertexOffset;
SubmeshGeometry gridSubmesh;
gridSubmesh.IndexCount = (UINT)grid.Indices32.size();
gridSubmesh.StartIndexLocation = gridIndexOffset;
gridSubmesh.BaseVertexLocation = gridVertexOffset;
SubmeshGeometry sphereSubmesh;
sphereSubmesh.IndexCount = (UINT)sphere.Indices32.size();
sphereSubmesh.StartIndexLocation = sphereIndexOffset;
sphereSubmesh.BaseVertexLocation = sphereVertexOffset;
SubmeshGeometry cylinderSubmesh;
cylinderSubmesh.IndexCount = (UINT)cylinder.Indices32.size();
cylinderSubmesh.StartIndexLocation = cylinderIndexOffset;
cylinderSubmesh.BaseVertexLocation = cylinderVertexOffset;
SubmeshGeometry quadSubmesh;
quadSubmesh.IndexCount = (UINT)quad.Indices32.size();
quadSubmesh.StartIndexLocation = quadIndexOffset;
quadSubmesh.BaseVertexLocation = quadVertexOffset;
//
// Extract the vertex elements we are interested in and pack the
// vertices of all the meshes into one vertex buffer.
//
auto totalVertexCount =
box.Vertices.size() +
grid.Vertices.size() +
sphere.Vertices.size() +
cylinder.Vertices.size() +
quad.Vertices.size();
std::vector<Vertex> vertices(totalVertexCount);
UINT k = 0;
for(size_t i = 0; i < box.Vertices.size(); ++i, ++k)
{
vertices[k].Pos = box.Vertices[i].Position;
vertices[k].Normal = box.Vertices[i].Normal;
vertices[k].TexC = box.Vertices[i].TexC;
vertices[k].TangentU = box.Vertices[i].TangentU;
}
for(size_t i = 0; i < grid.Vertices.size(); ++i, ++k)
{
vertices[k].Pos = grid.Vertices[i].Position;
vertices[k].Normal = grid.Vertices[i].Normal;
vertices[k].TexC = grid.Vertices[i].TexC;
vertices[k].TangentU = grid.Vertices[i].TangentU;
}
for(size_t i = 0; i < sphere.Vertices.size(); ++i, ++k)
{
vertices[k].Pos = sphere.Vertices[i].Position;
vertices[k].Normal = sphere.Vertices[i].Normal;
vertices[k].TexC = sphere.Vertices[i].TexC;
vertices[k].TangentU = sphere.Vertices[i].TangentU;
}
for(size_t i = 0; i < cylinder.Vertices.size(); ++i, ++k)
{
vertices[k].Pos = cylinder.Vertices[i].Position;
vertices[k].Normal = cylinder.Vertices[i].Normal;
vertices[k].TexC = cylinder.Vertices[i].TexC;
vertices[k].TangentU = cylinder.Vertices[i].TangentU;
}
for(int i = 0; i < quad.Vertices.size(); ++i, ++k)
{
vertices[k].Pos = quad.Vertices[i].Position;
vertices[k].Normal = quad.Vertices[i].Normal;
vertices[k].TexC = quad.Vertices[i].TexC;
vertices[k].TangentU = quad.Vertices[i].TangentU;
}
std::vector<std::uint16_t> indices;
indices.insert(indices.end(), std::begin(box.GetIndices16()), std::end(box.GetIndices16()));
indices.insert(indices.end(), std::begin(grid.GetIndices16()), std::end(grid.GetIndices16()));
indices.insert(indices.end(), std::begin(sphere.GetIndices16()), std::end(sphere.GetIndices16()));
indices.insert(indices.end(), std::begin(cylinder.GetIndices16()), std::end(cylinder.GetIndices16()));
indices.insert(indices.end(), std::begin(quad.GetIndices16()), std::end(quad.GetIndices16()));
const UINT vbByteSize = (UINT)vertices.size() * sizeof(Vertex);
const UINT ibByteSize = (UINT)indices.size() * sizeof(std::uint16_t);
auto geo = std::make_unique<MeshGeometry>();
geo->Name = "shapeGeo";
ThrowIfFailed(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));
CopyMemory(geo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);
ThrowIfFailed(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
CopyMemory(geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);
geo->VertexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
mCommandList.Get(), vertices.data(), vbByteSize, geo->VertexBufferUploader);
geo->IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
mCommandList.Get(), indices.data(), ibByteSize, geo->IndexBufferUploader);
geo->VertexByteStride = sizeof(Vertex);
geo->VertexBufferByteSize = vbByteSize;
geo->IndexFormat = DXGI_FORMAT_R16_UINT;
geo->IndexBufferByteSize = ibByteSize;
geo->DrawArgs["box"] = boxSubmesh;
geo->DrawArgs["grid"] = gridSubmesh;
geo->DrawArgs["sphere"] = sphereSubmesh;
geo->DrawArgs["cylinder"] = cylinderSubmesh;
geo->DrawArgs["quad"] = quadSubmesh;
mGeometries[geo->Name] = std::move(geo);
}
void SkinnedMeshApp::LoadSkinnedModel()
{
std::vector<M3DLoader::SkinnedVertex> vertices;
std::vector<std::uint16_t> indices;
M3DLoader m3dLoader;
m3dLoader.LoadM3d(mSkinnedModelFilename, vertices, indices,
mSkinnedSubsets, mSkinnedMats, mSkinnedInfo);
mSkinnedModelInst = std::make_unique<SkinnedModelInstance>();
mSkinnedModelInst->SkinnedInfo = &mSkinnedInfo;
mSkinnedModelInst->FinalTransforms.resize(mSkinnedInfo.BoneCount());
mSkinnedModelInst->ClipName = "Take1";
mSkinnedModelInst->TimePos = 0.0f;
const UINT vbByteSize = (UINT)vertices.size() * sizeof(SkinnedVertex);
const UINT ibByteSize = (UINT)indices.size() * sizeof(std::uint16_t);
auto geo = std::make_unique<MeshGeometry>();
geo->Name = mSkinnedModelFilename;
ThrowIfFailed(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));
CopyMemory(geo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);
ThrowIfFailed(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
CopyMemory(geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);
geo->VertexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
mCommandList.Get(), vertices.data(), vbByteSize, geo->VertexBufferUploader);
geo->IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
mCommandList.Get(), indices.data(), ibByteSize, geo->IndexBufferUploader);
geo->VertexByteStride = sizeof(SkinnedVertex);
geo->VertexBufferByteSize = vbByteSize;
geo->IndexFormat = DXGI_FORMAT_R16_UINT;
geo->IndexBufferByteSize = ibByteSize;
for(UINT i = 0; i < (UINT)mSkinnedSubsets.size(); ++i)
{
SubmeshGeometry submesh;
std::string name = "sm_" + std::to_string(i);
submesh.IndexCount = (UINT)mSkinnedSubsets[i].FaceCount * 3;
submesh.StartIndexLocation = mSkinnedSubsets[i].FaceStart * 3;
submesh.BaseVertexLocation = 0;
geo->DrawArgs[name] = submesh;
}
mGeometries[geo->Name] = std::move(geo);
}
void SkinnedMeshApp::BuildPSOs()
{
D3D12_GRAPHICS_PIPELINE_STATE_DESC opaquePsoDesc;
//
// PSO for opaque objects.
//
ZeroMemory(&opaquePsoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
opaquePsoDesc.InputLayout = { mInputLayout.data(), (UINT)mInputLayout.size() };
opaquePsoDesc.pRootSignature = mRootSignature.Get();
opaquePsoDesc.VS =
{
reinterpret_cast<BYTE*>(mShaders["standardVS"]->GetBufferPointer()),
mShaders["standardVS"]->GetBufferSize()
};
opaquePsoDesc.PS =
{
reinterpret_cast<BYTE*>(mShaders["opaquePS"]->GetBufferPointer()),
mShaders["opaquePS"]->GetBufferSize()
};
opaquePsoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
opaquePsoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
opaquePsoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
opaquePsoDesc.SampleMask = UINT_MAX;
opaquePsoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
opaquePsoDesc.NumRenderTargets = 1;
opaquePsoDesc.RTVFormats[0] = mBackBufferFormat;
opaquePsoDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
opaquePsoDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
opaquePsoDesc.DSVFormat = mDepthStencilFormat;
ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&opaquePsoDesc, IID_PPV_ARGS(&mPSOs["opaque"])));
//
// PSO for skinned pass.
//
D3D12_GRAPHICS_PIPELINE_STATE_DESC skinnedOpaquePsoDesc = opaquePsoDesc;
skinnedOpaquePsoDesc.InputLayout = { mSkinnedInputLayout.data(), (UINT)mSkinnedInputLayout.size() };
skinnedOpaquePsoDesc.VS =
{
reinterpret_cast<BYTE*>(mShaders["skinnedVS"]->GetBufferPointer()),
mShaders["skinnedVS"]->GetBufferSize()
};
skinnedOpaquePsoDesc.PS =
{
reinterpret_cast<BYTE*>(mShaders["opaquePS"]->GetBufferPointer()),
mShaders["opaquePS"]->GetBufferSize()
};
ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&skinnedOpaquePsoDesc, IID_PPV_ARGS(&mPSOs["skinnedOpaque"])));
//
// PSO for shadow map pass.
//
D3D12_GRAPHICS_PIPELINE_STATE_DESC smapPsoDesc = opaquePsoDesc;
smapPsoDesc.RasterizerState.DepthBias = 100000;
smapPsoDesc.RasterizerState.DepthBiasClamp = 0.0f;
smapPsoDesc.RasterizerState.SlopeScaledDepthBias = 1.0f;
smapPsoDesc.pRootSignature = mRootSignature.Get();
smapPsoDesc.VS =
{
reinterpret_cast<BYTE*>(mShaders["shadowVS"]->GetBufferPointer()),
mShaders["shadowVS"]->GetBufferSize()
};
smapPsoDesc.PS =
{
reinterpret_cast<BYTE*>(mShaders["shadowOpaquePS"]->GetBufferPointer()),
mShaders["shadowOpaquePS"]->GetBufferSize()
};
// Shadow map pass does not have a render target.
smapPsoDesc.RTVFormats[0] = DXGI_FORMAT_UNKNOWN;
smapPsoDesc.NumRenderTargets = 0;
ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&smapPsoDesc, IID_PPV_ARGS(&mPSOs["shadow_opaque"])));
D3D12_GRAPHICS_PIPELINE_STATE_DESC skinnedSmapPsoDesc = smapPsoDesc;
skinnedSmapPsoDesc.InputLayout = { mSkinnedInputLayout.data(), (UINT)mSkinnedInputLayout.size() };
skinnedSmapPsoDesc.VS =
{
reinterpret_cast<BYTE*>(mShaders["skinnedShadowVS"]->GetBufferPointer()),
mShaders["skinnedShadowVS"]->GetBufferSize()
};
skinnedSmapPsoDesc.PS =
{
reinterpret_cast<BYTE*>(mShaders["shadowOpaquePS"]->GetBufferPointer()),
mShaders["shadowOpaquePS"]->GetBufferSize()
};
ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&skinnedSmapPsoDesc, IID_PPV_ARGS(&mPSOs["skinnedShadow_opaque"])));
//
// PSO for debug layer.
//
D3D12_GRAPHICS_PIPELINE_STATE_DESC debugPsoDesc = opaquePsoDesc;
debugPsoDesc.pRootSignature = mRootSignature.Get();
debugPsoDesc.VS =
{
reinterpret_cast<BYTE*>(mShaders["debugVS"]->GetBufferPointer()),
mShaders["debugVS"]->GetBufferSize()
};
debugPsoDesc.PS =
{
reinterpret_cast<BYTE*>(mShaders["debugPS"]->GetBufferPointer()),
mShaders["debugPS"]->GetBufferSize()
};
ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&debugPsoDesc, IID_PPV_ARGS(&mPSOs["debug"])));
//
// PSO for drawing normals.
//
D3D12_GRAPHICS_PIPELINE_STATE_DESC drawNormalsPsoDesc = opaquePsoDesc;
drawNormalsPsoDesc.VS =
{
reinterpret_cast<BYTE*>(mShaders["drawNormalsVS"]->GetBufferPointer()),
mShaders["drawNormalsVS"]->GetBufferSize()
};
drawNormalsPsoDesc.PS =
{
reinterpret_cast<BYTE*>(mShaders["drawNormalsPS"]->GetBufferPointer()),
mShaders["drawNormalsPS"]->GetBufferSize()
};
drawNormalsPsoDesc.RTVFormats[0] = Ssao::NormalMapFormat;
drawNormalsPsoDesc.SampleDesc.Count = 1;
drawNormalsPsoDesc.SampleDesc.Quality = 0;
drawNormalsPsoDesc.DSVFormat = mDepthStencilFormat;
ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&drawNormalsPsoDesc, IID_PPV_ARGS(&mPSOs["drawNormals"])));
D3D12_GRAPHICS_PIPELINE_STATE_DESC skinnedDrawNormalsPsoDesc = drawNormalsPsoDesc;
skinnedDrawNormalsPsoDesc.InputLayout = { mSkinnedInputLayout.data(), (UINT)mSkinnedInputLayout.size() };
skinnedDrawNormalsPsoDesc.VS =
{
reinterpret_cast<BYTE*>(mShaders["skinnedDrawNormalsVS"]->GetBufferPointer()),
mShaders["skinnedDrawNormalsVS"]->GetBufferSize()
};
skinnedDrawNormalsPsoDesc.PS =
{
reinterpret_cast<BYTE*>(mShaders["drawNormalsPS"]->GetBufferPointer()),
mShaders["drawNormalsPS"]->GetBufferSize()
};
ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&skinnedDrawNormalsPsoDesc, IID_PPV_ARGS(&mPSOs["skinnedDrawNormals"])));
//
// PSO for SSAO.
//
D3D12_GRAPHICS_PIPELINE_STATE_DESC ssaoPsoDesc = opaquePsoDesc;
ssaoPsoDesc.InputLayout = { nullptr, 0 };
ssaoPsoDesc.pRootSignature = mSsaoRootSignature.Get();
ssaoPsoDesc.VS =
{
reinterpret_cast<BYTE*>(mShaders["ssaoVS"]->GetBufferPointer()),
mShaders["ssaoVS"]->GetBufferSize()
};
ssaoPsoDesc.PS =
{
reinterpret_cast<BYTE*>(mShaders["ssaoPS"]->GetBufferPointer()),
mShaders["ssaoPS"]->GetBufferSize()
};
// SSAO effect does not need the depth buffer.
ssaoPsoDesc.DepthStencilState.DepthEnable = false;
ssaoPsoDesc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO;
ssaoPsoDesc.RTVFormats[0] = Ssao::AmbientMapFormat;
ssaoPsoDesc.SampleDesc.Count = 1;
ssaoPsoDesc.SampleDesc.Quality = 0;
ssaoPsoDesc.DSVFormat = DXGI_FORMAT_UNKNOWN;
ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&ssaoPsoDesc, IID_PPV_ARGS(&mPSOs["ssao"])));
//
// PSO for SSAO blur.
//
D3D12_GRAPHICS_PIPELINE_STATE_DESC ssaoBlurPsoDesc = ssaoPsoDesc;
ssaoBlurPsoDesc.VS =
{
reinterpret_cast<BYTE*>(mShaders["ssaoBlurVS"]->GetBufferPointer()),
mShaders["ssaoBlurVS"]->GetBufferSize()
};
ssaoBlurPsoDesc.PS =
{
reinterpret_cast<BYTE*>(mShaders["ssaoBlurPS"]->GetBufferPointer()),
mShaders["ssaoBlurPS"]->GetBufferSize()
};
ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&ssaoBlurPsoDesc, IID_PPV_ARGS(&mPSOs["ssaoBlur"])));
//
// PSO for sky.
//
D3D12_GRAPHICS_PIPELINE_STATE_DESC skyPsoDesc = opaquePsoDesc;
// The camera is inside the sky sphere, so just turn off culling.
skyPsoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
// Make sure the depth function is LESS_EQUAL and not just LESS.
// Otherwise, the normalized depth values at z = 1 (NDC) will
// fail the depth test if the depth buffer was cleared to 1.
skyPsoDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
skyPsoDesc.pRootSignature = mRootSignature.Get();
skyPsoDesc.VS =
{
reinterpret_cast<BYTE*>(mShaders["skyVS"]->GetBufferPointer()),
mShaders["skyVS"]->GetBufferSize()
};
skyPsoDesc.PS =
{
reinterpret_cast<BYTE*>(mShaders["skyPS"]->GetBufferPointer()),
mShaders["skyPS"]->GetBufferSize()
};
ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&skyPsoDesc, IID_PPV_ARGS(&mPSOs["sky"])));
}
void SkinnedMeshApp::BuildFrameResources()
{
for(int i = 0; i < gNumFrameResources; ++i)
{
mFrameResources.push_back(std::make_unique<FrameResource>(md3dDevice.Get(),
2, (UINT)mAllRitems.size(),
1,
(UINT)mMaterials.size()));
}
}
void SkinnedMeshApp::BuildMaterials()
{
auto bricks0 = std::make_unique<Material>();
bricks0->Name = "bricks0";
bricks0->MatCBIndex = 0;
bricks0->DiffuseSrvHeapIndex = 0;
bricks0->NormalSrvHeapIndex = 1;
bricks0->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
bricks0->FresnelR0 = XMFLOAT3(0.1f, 0.1f, 0.1f);
bricks0->Roughness = 0.3f;
auto tile0 = std::make_unique<Material>();
tile0->Name = "tile0";
tile0->MatCBIndex = 1;
tile0->DiffuseSrvHeapIndex = 2;
tile0->NormalSrvHeapIndex = 3;
tile0->DiffuseAlbedo = XMFLOAT4(0.9f, 0.9f, 0.9f, 1.0f);
tile0->FresnelR0 = XMFLOAT3(0.2f, 0.2f, 0.2f);
tile0->Roughness = 0.1f;
auto mirror0 = std::make_unique<Material>();
mirror0->Name = "mirror0";
mirror0->MatCBIndex = 2;
mirror0->DiffuseSrvHeapIndex = 4;
mirror0->NormalSrvHeapIndex = 5;
mirror0->DiffuseAlbedo = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f);
mirror0->FresnelR0 = XMFLOAT3(0.98f, 0.97f, 0.95f);
mirror0->Roughness = 0.1f;
auto sky = std::make_unique<Material>();
sky->Name = "sky";
sky->MatCBIndex = 3;
sky->DiffuseSrvHeapIndex = 6;
sky->NormalSrvHeapIndex = 7;
sky->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
sky->FresnelR0 = XMFLOAT3(0.1f, 0.1f, 0.1f);
sky->Roughness = 1.0f;
mMaterials["bricks0"] = std::move(bricks0);
mMaterials["tile0"] = std::move(tile0);
mMaterials["mirror0"] = std::move(mirror0);
mMaterials["sky"] = std::move(sky);
UINT matCBIndex = 4;
UINT srvHeapIndex = mSkinnedSrvHeapStart;
for(UINT i = 0; i < mSkinnedMats.size(); ++i)
{
auto mat = std::make_unique<Material>();
mat->Name = mSkinnedMats[i].Name;
mat->MatCBIndex = matCBIndex++;
mat->DiffuseSrvHeapIndex = srvHeapIndex++;
mat->NormalSrvHeapIndex = srvHeapIndex++;
mat->DiffuseAlbedo = mSkinnedMats[i].DiffuseAlbedo;
mat->FresnelR0 = mSkinnedMats[i].FresnelR0;
mat->Roughness = mSkinnedMats[i].Roughness;
mMaterials[mat->Name] = std::move(mat);
}
}
void SkinnedMeshApp::BuildRenderItems()
{
auto skyRitem = std::make_unique<RenderItem>();
XMStoreFloat4x4(&skyRitem->World, XMMatrixScaling(5000.0f, 5000.0f, 5000.0f));
skyRitem->TexTransform = MathHelper::Identity4x4();
skyRitem->ObjCBIndex = 0;
skyRitem->Mat = mMaterials["sky"].get();
skyRitem->Geo = mGeometries["shapeGeo"].get();
skyRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
skyRitem->IndexCount = skyRitem->Geo->DrawArgs["sphere"].IndexCount;
skyRitem->StartIndexLocation = skyRitem->Geo->DrawArgs["sphere"].StartIndexLocation;
skyRitem->BaseVertexLocation = skyRitem->Geo->DrawArgs["sphere"].BaseVertexLocation;
mRitemLayer[(int)RenderLayer::Sky].push_back(skyRitem.get());
mAllRitems.push_back(std::move(skyRitem));
auto quadRitem = std::make_unique<RenderItem>();
quadRitem->World = MathHelper::Identity4x4();
quadRitem->TexTransform = MathHelper::Identity4x4();
quadRitem->ObjCBIndex = 1;
quadRitem->Mat = mMaterials["bricks0"].get();
quadRitem->Geo = mGeometries["shapeGeo"].get();
quadRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
quadRitem->IndexCount = quadRitem->Geo->DrawArgs["quad"].IndexCount;
quadRitem->StartIndexLocation = quadRitem->Geo->DrawArgs["quad"].StartIndexLocation;
quadRitem->BaseVertexLocation = quadRitem->Geo->DrawArgs["quad"].BaseVertexLocation;
mRitemLayer[(int)RenderLayer::Debug].push_back(quadRitem.get());
mAllRitems.push_back(std::move(quadRitem));
auto boxRitem = std::make_unique<RenderItem>();
XMStoreFloat4x4(&boxRitem->World, XMMatrixScaling(2.0f, 1.0f, 2.0f)*XMMatrixTranslation(0.0f, 0.5f, 0.0f));
XMStoreFloat4x4(&boxRitem->TexTransform, XMMatrixScaling(1.0f, 1.0f, 1.0f));
boxRitem->ObjCBIndex = 2;
boxRitem->Mat = mMaterials["bricks0"].get();
boxRitem->Geo = mGeometries["shapeGeo"].get();
boxRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
boxRitem->IndexCount = boxRitem->Geo->DrawArgs["box"].IndexCount;
boxRitem->StartIndexLocation = boxRitem->Geo->DrawArgs["box"].StartIndexLocation;
boxRitem->BaseVertexLocation = boxRitem->Geo->DrawArgs["box"].BaseVertexLocation;
mRitemLayer[(int)RenderLayer::Opaque].push_back(boxRitem.get());
mAllRitems.push_back(std::move(boxRitem));
auto gridRitem = std::make_unique<RenderItem>();
gridRitem->World = MathHelper::Identity4x4();
XMStoreFloat4x4(&gridRitem->TexTransform, XMMatrixScaling(8.0f, 8.0f, 1.0f));
gridRitem->ObjCBIndex = 3;
gridRitem->Mat = mMaterials["tile0"].get();
gridRitem->Geo = mGeometries["shapeGeo"].get();
gridRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
gridRitem->IndexCount = gridRitem->Geo->DrawArgs["grid"].IndexCount;
gridRitem->StartIndexLocation = gridRitem->Geo->DrawArgs["grid"].StartIndexLocation;
gridRitem->BaseVertexLocation = gridRitem->Geo->DrawArgs["grid"].BaseVertexLocation;
mRitemLayer[(int)RenderLayer::Opaque].push_back(gridRitem.get());
mAllRitems.push_back(std::move(gridRitem));
XMMATRIX brickTexTransform = XMMatrixScaling(1.5f, 2.0f, 1.0f);
UINT objCBIndex = 4;
for(int i = 0; i < 5; ++i)
{
auto leftCylRitem = std::make_unique<RenderItem>();
auto rightCylRitem = std::make_unique<RenderItem>();
auto leftSphereRitem = std::make_unique<RenderItem>();
auto rightSphereRitem = std::make_unique<RenderItem>();
XMMATRIX leftCylWorld = XMMatrixTranslation(-5.0f, 1.5f, -10.0f + i*5.0f);
XMMATRIX rightCylWorld = XMMatrixTranslation(+5.0f, 1.5f, -10.0f + i*5.0f);
XMMATRIX leftSphereWorld = XMMatrixTranslation(-5.0f, 3.5f, -10.0f + i*5.0f);
XMMATRIX rightSphereWorld = XMMatrixTranslation(+5.0f, 3.5f, -10.0f + i*5.0f);
XMStoreFloat4x4(&leftCylRitem->World, rightCylWorld);
XMStoreFloat4x4(&leftCylRitem->TexTransform, brickTexTransform);
leftCylRitem->ObjCBIndex = objCBIndex++;
leftCylRitem->Mat = mMaterials["bricks0"].get();
leftCylRitem->Geo = mGeometries["shapeGeo"].get();
leftCylRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
leftCylRitem->IndexCount = leftCylRitem->Geo->DrawArgs["cylinder"].IndexCount;
leftCylRitem->StartIndexLocation = leftCylRitem->Geo->DrawArgs["cylinder"].StartIndexLocation;
leftCylRitem->BaseVertexLocation = leftCylRitem->Geo->DrawArgs["cylinder"].BaseVertexLocation;
XMStoreFloat4x4(&rightCylRitem->World, leftCylWorld);
XMStoreFloat4x4(&rightCylRitem->TexTransform, brickTexTransform);
rightCylRitem->ObjCBIndex = objCBIndex++;
rightCylRitem->Mat = mMaterials["bricks0"].get();
rightCylRitem->Geo = mGeometries["shapeGeo"].get();
rightCylRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
rightCylRitem->IndexCount = rightCylRitem->Geo->DrawArgs["cylinder"].IndexCount;
rightCylRitem->StartIndexLocation = rightCylRitem->Geo->DrawArgs["cylinder"].StartIndexLocation;
rightCylRitem->BaseVertexLocation = rightCylRitem->Geo->DrawArgs["cylinder"].BaseVertexLocation;
XMStoreFloat4x4(&leftSphereRitem->World, leftSphereWorld);
leftSphereRitem->TexTransform = MathHelper::Identity4x4();
leftSphereRitem->ObjCBIndex = objCBIndex++;
leftSphereRitem->Mat = mMaterials["mirror0"].get();
leftSphereRitem->Geo = mGeometries["shapeGeo"].get();
leftSphereRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
leftSphereRitem->IndexCount = leftSphereRitem->Geo->DrawArgs["sphere"].IndexCount;
leftSphereRitem->StartIndexLocation = leftSphereRitem->Geo->DrawArgs["sphere"].StartIndexLocation;
leftSphereRitem->BaseVertexLocation = leftSphereRitem->Geo->DrawArgs["sphere"].BaseVertexLocation;
XMStoreFloat4x4(&rightSphereRitem->World, rightSphereWorld);
rightSphereRitem->TexTransform = MathHelper::Identity4x4();
rightSphereRitem->ObjCBIndex = objCBIndex++;
rightSphereRitem->Mat = mMaterials["mirror0"].get();
rightSphereRitem->Geo = mGeometries["shapeGeo"].get();
rightSphereRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
rightSphereRitem->IndexCount = rightSphereRitem->Geo->DrawArgs["sphere"].IndexCount;
rightSphereRitem->StartIndexLocation = rightSphereRitem->Geo->DrawArgs["sphere"].StartIndexLocation;
rightSphereRitem->BaseVertexLocation = rightSphereRitem->Geo->DrawArgs["sphere"].BaseVertexLocation;
mRitemLayer[(int)RenderLayer::Opaque].push_back(leftCylRitem.get());
mRitemLayer[(int)RenderLayer::Opaque].push_back(rightCylRitem.get());
mRitemLayer[(int)RenderLayer::Opaque].push_back(leftSphereRitem.get());
mRitemLayer[(int)RenderLayer::Opaque].push_back(rightSphereRitem.get());
mAllRitems.push_back(std::move(leftCylRitem));
mAllRitems.push_back(std::move(rightCylRitem));
mAllRitems.push_back(std::move(leftSphereRitem));
mAllRitems.push_back(std::move(rightSphereRitem));
}
for(UINT i = 0; i < mSkinnedMats.size(); ++i)
{
std::string submeshName = "sm_" + std::to_string(i);
auto ritem = std::make_unique<RenderItem>();
// Reflect to change coordinate system from the RHS the data was exported out as.
XMMATRIX modelScale = XMMatrixScaling(0.05f, 0.05f, -0.05f);
XMMATRIX modelRot = XMMatrixRotationY(MathHelper::Pi);
XMMATRIX modelOffset = XMMatrixTranslation(0.0f, 0.0f, -5.0f);
XMStoreFloat4x4(&ritem->World, modelScale*modelRot*modelOffset);
ritem->TexTransform = MathHelper::Identity4x4();
ritem->ObjCBIndex = objCBIndex++;
ritem->Mat = mMaterials[mSkinnedMats[i].Name].get();
ritem->Geo = mGeometries[mSkinnedModelFilename].get();
ritem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
ritem->IndexCount = ritem->Geo->DrawArgs[submeshName].IndexCount;
ritem->StartIndexLocation = ritem->Geo->DrawArgs[submeshName].StartIndexLocation;
ritem->BaseVertexLocation = ritem->Geo->DrawArgs[submeshName].BaseVertexLocation;
// All render items for this solider.m3d instance share
// the same skinned model instance.
ritem->SkinnedCBIndex = 0;
ritem->SkinnedModelInst = mSkinnedModelInst.get();
mRitemLayer[(int)RenderLayer::SkinnedOpaque].push_back(ritem.get());
mAllRitems.push_back(std::move(ritem));
}
}
void SkinnedMeshApp::DrawRenderItems(ID3D12GraphicsCommandList* cmdList, const std::vector<RenderItem*>& ritems)
{
UINT objCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));
UINT skinnedCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(SkinnedConstants));
auto objectCB = mCurrFrameResource->ObjectCB->Resource();
auto skinnedCB = mCurrFrameResource->SkinnedCB->Resource();
// For each render item...
for(size_t i = 0; i < ritems.size(); ++i)
{
auto ri = ritems[i];
cmdList->IASetVertexBuffers(0, 1, &ri->Geo->VertexBufferView());
cmdList->IASetIndexBuffer(&ri->Geo->IndexBufferView());
cmdList->IASetPrimitiveTopology(ri->PrimitiveType);
D3D12_GPU_VIRTUAL_ADDRESS objCBAddress = objectCB->GetGPUVirtualAddress() + ri->ObjCBIndex*objCBByteSize;
cmdList->SetGraphicsRootConstantBufferView(0, objCBAddress);
if(ri->SkinnedModelInst != nullptr)
{
D3D12_GPU_VIRTUAL_ADDRESS skinnedCBAddress = skinnedCB->GetGPUVirtualAddress() + ri->SkinnedCBIndex*skinnedCBByteSize;
cmdList->SetGraphicsRootConstantBufferView(1, skinnedCBAddress);
}
else
{
cmdList->SetGraphicsRootConstantBufferView(1, 0);
}
cmdList->DrawIndexedInstanced(ri->IndexCount, 1, ri->StartIndexLocation, ri->BaseVertexLocation, 0);
}
}
void SkinnedMeshApp::DrawSceneToShadowMap()
{
mCommandList->RSSetViewports(1, &mShadowMap->Viewport());
mCommandList->RSSetScissorRects(1, &mShadowMap->ScissorRect());
// Change to DEPTH_WRITE.
mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(mShadowMap->Resource(),
D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_STATE_DEPTH_WRITE));
// Clear the back buffer and depth buffer.
mCommandList->ClearDepthStencilView(mShadowMap->Dsv(),
D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);
// Specify the buffers we are going to render to.
mCommandList->OMSetRenderTargets(0, nullptr, false, &mShadowMap->Dsv());
// Bind the pass constant buffer for the shadow map pass.
UINT passCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(PassConstants));
auto passCB = mCurrFrameResource->PassCB->Resource();
D3D12_GPU_VIRTUAL_ADDRESS passCBAddress = passCB->GetGPUVirtualAddress() + 1*passCBByteSize;
mCommandList->SetGraphicsRootConstantBufferView(2, passCBAddress);
mCommandList->SetPipelineState(mPSOs["shadow_opaque"].Get());
DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Opaque]);
mCommandList->SetPipelineState(mPSOs["skinnedShadow_opaque"].Get());
DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::SkinnedOpaque]);
// Change back to GENERIC_READ so we can read the texture in a shader.
mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(mShadowMap->Resource(),
D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_GENERIC_READ));
}
void SkinnedMeshApp::DrawNormalsAndDepth()
{
mCommandList->RSSetViewports(1, &mScreenViewport);
mCommandList->RSSetScissorRects(1, &mScissorRect);
auto normalMap = mSsao->NormalMap();
auto normalMapRtv = mSsao->NormalMapRtv();
// Change to RENDER_TARGET.
mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(normalMap,
D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_STATE_RENDER_TARGET));
// Clear the screen normal map and depth buffer.
float clearValue[] = {0.0f, 0.0f, 1.0f, 0.0f};
mCommandList->ClearRenderTargetView(normalMapRtv, clearValue, 0, nullptr);
mCommandList->ClearDepthStencilView(DepthStencilView(), D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);
// Specify the buffers we are going to render to.
mCommandList->OMSetRenderTargets(1, &normalMapRtv, true, &DepthStencilView());
// Bind the constant buffer for this pass.
auto passCB = mCurrFrameResource->PassCB->Resource();
mCommandList->SetGraphicsRootConstantBufferView(2, passCB->GetGPUVirtualAddress());
mCommandList->SetPipelineState(mPSOs["drawNormals"].Get());
DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Opaque]);
mCommandList->SetPipelineState(mPSOs["skinnedDrawNormals"].Get());
DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::SkinnedOpaque]);
// Change back to GENERIC_READ so we can read the texture in a shader.
mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(normalMap,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_GENERIC_READ));
}
CD3DX12_CPU_DESCRIPTOR_HANDLE SkinnedMeshApp::GetCpuSrv(int index)const
{
auto srv = CD3DX12_CPU_DESCRIPTOR_HANDLE(mSrvDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
srv.Offset(index, mCbvSrvUavDescriptorSize);
return srv;
}
CD3DX12_GPU_DESCRIPTOR_HANDLE SkinnedMeshApp::GetGpuSrv(int index)const
{
auto srv = CD3DX12_GPU_DESCRIPTOR_HANDLE(mSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart());
srv.Offset(index, mCbvSrvUavDescriptorSize);
return srv;
}
CD3DX12_CPU_DESCRIPTOR_HANDLE SkinnedMeshApp::GetDsv(int index)const
{
auto dsv = CD3DX12_CPU_DESCRIPTOR_HANDLE(mDsvHeap->GetCPUDescriptorHandleForHeapStart());
dsv.Offset(index, mDsvDescriptorSize);
return dsv;
}
CD3DX12_CPU_DESCRIPTOR_HANDLE SkinnedMeshApp::GetRtv(int index)const
{
auto rtv = CD3DX12_CPU_DESCRIPTOR_HANDLE(mRtvHeap->GetCPUDescriptorHandleForHeapStart());
rtv.Offset(index, mRtvDescriptorSize);
return rtv;
}
std::array<const CD3DX12_STATIC_SAMPLER_DESC, 7> SkinnedMeshApp::GetStaticSamplers()
{
// Applications usually only need a handful of samplers. So just define them all up front
// and keep them available as part of the root signature.
const CD3DX12_STATIC_SAMPLER_DESC pointWrap(
0, // shaderRegister
D3D12_FILTER_MIN_MAG_MIP_POINT, // filter
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_WRAP); // addressW
const CD3DX12_STATIC_SAMPLER_DESC pointClamp(
1, // shaderRegister
D3D12_FILTER_MIN_MAG_MIP_POINT, // filter
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_CLAMP); // addressW
const CD3DX12_STATIC_SAMPLER_DESC linearWrap(
2, // shaderRegister
D3D12_FILTER_MIN_MAG_MIP_LINEAR, // filter
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_WRAP); // addressW
const CD3DX12_STATIC_SAMPLER_DESC linearClamp(
3, // shaderRegister
D3D12_FILTER_MIN_MAG_MIP_LINEAR, // filter
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_CLAMP); // addressW
const CD3DX12_STATIC_SAMPLER_DESC anisotropicWrap(
4, // shaderRegister
D3D12_FILTER_ANISOTROPIC, // filter
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressW
0.0f, // mipLODBias
8); // maxAnisotropy
const CD3DX12_STATIC_SAMPLER_DESC anisotropicClamp(
5, // shaderRegister
D3D12_FILTER_ANISOTROPIC, // filter
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressW
0.0f, // mipLODBias
8); // maxAnisotropy
const CD3DX12_STATIC_SAMPLER_DESC shadow(
6, // shaderRegister
D3D12_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT, // filter
D3D12_TEXTURE_ADDRESS_MODE_BORDER, // addressU
D3D12_TEXTURE_ADDRESS_MODE_BORDER, // addressV
D3D12_TEXTURE_ADDRESS_MODE_BORDER, // addressW
0.0f, // mipLODBias
16, // maxAnisotropy
D3D12_COMPARISON_FUNC_LESS_EQUAL,
D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK);
return {
pointWrap, pointClamp,
linearWrap, linearClamp,
anisotropicWrap, anisotropicClamp,
shadow
};
}